<?php
/**
 * Description: 能力计算
 * User: devforma
 * Date: 15/4/7
 * Time: 11:00
 */


/**
 * 获取用户信息
 * 
 * @param int $userId 用户id
 * @return null|Object
 */
function getUserData($userId) {
    global $cmsdb;
    
    $cacheKey = 'qb_userData:'.$userId;
    if (($value = cacheGet($cacheKey)) != null) {
        return $value;
    } else {
        $value = $cmsdb->getRow('SELECT * FROM `qb_user_data` WHERE `user_id`=:user_id', array(':user_id' => $userId));
        if (!empty($value)) {
            cacheAdd($cacheKey, $value, 24*60*60); //24小时缓存时间

            return $value;
        }
    }
    return null;
}

/**
 * 根据知识点id抽题
 *
 * @param array $noteArray 知识点数组
 * @param int $totalCount 抽题总数
 * @param int $userId 用户id
 * @param bool $byGroup 是否按照成组的抽题，默认false
 * @return array
 */
function getNoteQuestions($noteArray, $totalCount, $userId, $byGroup = false) {
    global $cmsdb, $uddb;

    if (!is_array($noteArray) || empty($noteArray))
        return array();

    $noteIdStr = implode(',', $noteArray);

    if ($byGroup) {
        //增加套题筛选的功能，同一个套题
        $sql = "SELECT q.id ,q.material_id FROM `relation_question_note` AS n INNER JOIN `original_ztk_questions` AS q
         ON n.question_id=q.id WHERE n.note_id IN ({$noteIdStr}) AND q.type=1 AND q.is_handpick=1";

        $tmpQs = $cmsdb->getResults($sql);

        if (count($tmpQs) > $totalCount) {
            //获取用户已做过的题目id
            $table = $uddb->table($uddb->qbUserTests)->sublist($userId);
            $sql = "SELECT DISTINCT(`question_id`) FROM `{$table}` WHERE `user_id`=:user_id AND
             `note_id` IN ({$noteIdStr})";

            $doneIds = $uddb->getCol($sql, array(':user_id' => $userId));
            if (!empty($doneIds)) {
                $qs = array();
                foreach ($tmpQs as $q) {
                    if (!in_array($q->id, $doneIds, true))
                        $qs[] = $q;
                }

                $qsCount = count($qs);
                if ($qsCount > 0 && $qsCount < $totalCount) { //未做过的题目不够抽
                    $keys = array_rand($tmpQs, $totalCount-$qsCount);
                    foreach ($keys as $key) {
                        $qs[] = $tmpQs[$key];
                    }

                } elseif ($qsCount == 0) { //全做过
                    $qs = $tmpQs;
                }

            } else {
                $qs = $tmpQs;
            }

        } else {
            $qs = $tmpQs;
        }

        $ids = getResultQuestionIds($qs, $totalCount);
        return getQuestionsByIds($ids);
    }
    
    $sql = "SELECT q.id FROM `relation_question_note` AS n INNER JOIN `original_ztk_questions` AS q
         ON n.question_id=q.id WHERE n.note_id IN ({$noteIdStr}) AND q.type=1 AND q.is_handpick=1";
    $qids = $cmsdb->getCol($sql);

    if ($totalCount > count($qids)) {
        return getQuestionsByIds($qids);
    } else {
        shuffle($qids);
        $newIds = array();
        $keys = array_rand($qids, $totalCount);
        foreach ($keys as $key) {
            $newIds[] = $qids[$key];
        }
        return getQuestionsByIds($newIds);
    }
}

function getResultQuestionIds($materialQuestions, $count) {

    $ids = array();
    if (empty($materialQuestions) || count($materialQuestions) <= $count){
        foreach($materialQuestions as $mM)
            $ids[] = $mM ->id;
        return $ids;
    }

    shuffle($materialQuestions);

    //遍历结果集，取前totalCount项，如果是材料题，把所有材料题取出来
    $materialPlus = 0;
    foreach ($materialQuestions as $qs) {

        if (count($ids) >= $count)
            break;

        if ( !empty($qs -> material_id)) {

            $materialPlus ++;
            if ($materialPlus < 5) {
                continue;
            }
            $mArray  = array();//这个材料题的id；
            foreach($materialQuestions as $mM) {
                if($mM -> material_id == $qs -> material_id) {
                    $mArray[] = $mM->id;
                }
            }
            $mArray = array_diff($mArray,$ids);//去掉之前重复的id
            if (count($ids) + count($mArray) > $count) {
                continue;
            } else {
                $materialPlus = 0;
                $ids = array_merge($ids,$mArray);//合并到结果集中
            }
        } else {
            $ids[] = $qs -> id;
        }
    }

    return $ids;
}

/**
 * 根据试卷类型名称或ID获取类型描述
 * 
 * @param string $NameOrId 类型名称或id
 * @return Object
 */
function getPaperType($NameOrId) {
    global $cmsdb;
    
    $cacheKey = 'qb_paperType';
    if (($value = cacheGet($cacheKey)) != null) {
        return $value[$NameOrId];
    } else {
        $value = array();
        $types = $cmsdb->getResults('SELECT * FROM `qb_paper_type` WHERE 1');
        foreach ($types as $type) {
            $value["$type->type_name"] = $type;
            $value["$type->type_id"] = $type;
        }

        cacheAdd($cacheKey, $value, 48*60*60);

        return $value[$NameOrId];
    }
}


/**
 * 根据知识点学习情况计算能力评估分
 * @param array $noteInfoArray 知识点信息数组
 * @param int $examId 考试项目ID
 * @return float 最终估分
 */
function calEvaluationScore(&$noteInfoArray, $examId = 0) {
    global $cmsdb;
    
    if (!is_array($noteInfoArray) || empty($noteInfoArray) || $examId==0)
        return 0.0;

    /* 2015-10-27 特殊考试项目均采用国考的知识点体系 */
    if (in_array(intval($examId), array(115, 889, 1087, 1177, 902), true)) {
        $examId = 1126;
    }
    
    $testareaId = $cmsdb->getVar('SELECT exam_id FROM data_exam_detail WHERE id='.$examId);
    if (empty($testareaId)) return 0.0;
    
    $results = getNotesByTestId($testareaId);
    if (empty($results)) return 0.0;

    $notes = array();
    foreach ($results as $rs) {
       $notes[$rs->note_id]['rate'] = $rs->rate;
    }

    $sum = 0.0;
    //数据库存储结构转换为可读的结构字段列表
    $structureKeys = array('value', 'rightCount', 'totalCount', 'level', 'recomend');

    foreach ($noteInfoArray as $key => $noteInfo) {
        if (!isset($notes[$key])) continue;

        //数据库存储结构转换为可读的结构
        $noteInfo = transformDataStructure($noteInfo, $structureKeys);
        $totalNum = $noteInfo['totalCount'];
        $rightNum = $noteInfo['rightCount'];
        //如果该知识点下的做题数量不足10道,补充到10,并且按照25%的正确率补充题目
        if ($totalNum < 10) {
            $totalNum = 10;
            $rightNum += round($totalNum * 0.25 - $noteInfo['totalCount'] * 0.25);
        }
        $sum += $notes[$key]['rate'] * $noteInfo['value'] * ($rightNum / $totalNum);
    }

    return round($sum);
}


/**
 * 知识点掌握水平变化计算
 *
 * @param array $noteInfoArray 知识点信息数组（传引用）
 * @param array $changedNoteArray 做题遇到的知识点信息
 * @param int $examDetailId exam_detail_id
 * @return array 能力有变化的知识点数组
 */
function calNoteLevel(&$noteInfoArray, $changedNoteArray, $examDetailId) {

    if (!is_array($noteInfoArray) || empty($noteInfoArray) || !is_array($noteInfoArray)
        || empty($noteInfoArray))
        return array();

    $noteChanged = array();
    $notesAccuracy = array();

    //新版知识点结构
    $noteHierarchy = getNewNoteHierarchy();
    
    $examId = getExamByExamDetail($examDetailId);
    if ($examId && $results = getNotesByTestId($examId)) {
        foreach ($results as $rs) $notesAccuracy[$rs->note_id] = $rs->target_accuracy;
    }
    
    $UserNoteIds = array_keys($noteInfoArray);

    //数据库存储结构转换为可读的结构字段列表
    $structureKeys = array('value', 'rightCount', 'totalCount', 'level', 'recomend');

    foreach ($changedNoteArray as $noteId => $note) {
        if (!in_array($noteId, $UserNoteIds)) //做题的知识点不在用户所选考试项目的知识点内
            continue;

        //获取知识点名称
        if (isset($noteHierarchy[$noteId]))
            $noteName = strval($noteHierarchy[$noteId]['name']);
        else
            $noteName = '';

        //数据库存储结构转换为可读的结构
        $noteInfoArray[$noteId] = transformDataStructure($noteInfoArray[$noteId], $structureKeys);

        $noteInfoArray[$noteId]['totalCount'] += $note['total'];
        $noteInfoArray[$noteId]['rightCount'] += $note['right'];

        if ($noteInfoArray[$noteId]['level'] == 0) { //该知识点第一次遇到
            $noteChanged[] = array(
                'id' => $noteId,
                'name' => $noteName,
                'from' => 0, //初始为0
                'to' => ++$noteInfoArray[$noteId]['level']
            );

        } elseif ($noteInfoArray[$noteId]['totalCount'] < 30) { //该知识点已经做过且做题数小于30
            $noteInfoArray[$noteId]['level'] = 1;

        } elseif ($noteInfoArray[$noteId]['totalCount'] >= 30) { //该知识点做题数不小于30

            if (isset($notesAccuracy[$noteId])) {
                $targetAccuracy = $notesAccuracy[$noteId];
            } else {
                $targetAccuracy = 1;
            }

            //2016-04-20 新的知识点正确率逻辑
            if ($note['total'] < 30) {
                $currAccuracy = ($note['right'] + (30 - $note['total']) * $noteInfoArray[$noteId]['level'] * $targetAccuracy / 5) / 30;
            } else {
                $currAccuracy = $note['right'] / $note['total'];
            }

            if ($currAccuracy < $targetAccuracy) { //未达到目标正确率
                $currLevel = round(4*($currAccuracy-$targetAccuracy)/$targetAccuracy) + 5;

                if ($currLevel > $noteInfoArray[$noteId]['level']
                    && $noteInfoArray[$noteId]['level'] < 5) { //当前计算的level大于之前的level并且之前的level小于5
                    $noteChanged[] = array(
                        'id' => $noteId,
                        'name' => $noteName,
                        'from' => $noteInfoArray[$noteId]['level'],
                        'to' => ++$noteInfoArray[$noteId]['level']
                    );

                } elseif ($currLevel < $noteInfoArray[$noteId]['level']
                    && $noteInfoArray[$noteId]['level'] > 1) { //当前计算的level小于之前的level且之前的level大于1
                    $noteChanged[] = array(
                        'id' => $noteId,
                        'name' => $noteName,
                        'from' => $noteInfoArray[$noteId]['level'],
                        'to' => --$noteInfoArray[$noteId]['level']
                    );
                }

            } elseif ($noteInfoArray[$noteId]['level'] < 5) { //达到目标正确率且之前的level小于5
                $noteChanged[] = array(
                    'id' => $noteId,
                    'name' => $noteName,
                    'from' => $noteInfoArray[$noteId]['level'],
                    'to' => ++$noteInfoArray[$noteId]['level']
                );
            }
        }

        //还原存储结构(去除显式key)
        $noteInfoArray[$noteId] = array_values($noteInfoArray[$noteId]);
    }

    return $noteChanged;
}


/**
 * 更新用户的估分排名
 *
 * @param $examDetailId
 * @param $oldScore
 * @param $newScore
 */
function updateScoreRank($examDetailId, $oldScore, $newScore) {
    $cacheKey = 'func:getEvaluateRank:'.$examDetailId;

    if ($rank = cacheGet($cacheKey)) {
        if (isset($rank[$oldScore]) && $rank[$oldScore] > 1)
            $rank[$oldScore]--;

        if (isset($rank[$newScore])) {
            $rank[$newScore]++;
        } else {
            $rank[$newScore] = 1;
        }

        cacheSet($cacheKey, $rank, 30*24*60*60);
    }
}


/**
 * 计算估分排名
 *
 * @param $examDetailId
 * @param $userScore
 * @return float
 */
function getEvaluateRank($examDetailId, $userScore) {
    global $cmsdb;

    $cacheKey = 'func:getEvaluateRank:'.$examDetailId;

    if (($rank = cacheGet($cacheKey)) == null) {
        $rank = array();

        //获取该考试项目下所有用户的估分
        $sql = "SELECT `score` FROM `qb_user_data` WHERE `exam_detail_id`={$examDetailId}";
        $data = (array)$cmsdb->getCol($sql);

        //按照分值进行人数统计
        foreach ($data as $score) {
            if (isset($rank[$score])) {
                $rank[$score]++;
            } else {
                $rank[$score] = 1;
            }
        }

        cacheAdd($cacheKey, $rank, 30*24*60*60);
    }

    //计算总人数和高于用户估分的人数
    $total = $higher = 0;
    foreach ($rank as $score => $persons) {
        $total += $persons;
        if ($score > $userScore) {
            $higher += $persons;
        }
    }

    //计算排在前百分之多少
    $result = $higher/$total;
    if ($result < 0.01)
        $result = 0.01;

    return $result;
}


/**
 * 获取mini模考信息
 *
 * @param $date
 * @return array|bool
 */
function getMiniMockInfo($date) {
    global $cmsdb;

    $cacheKey = 'func:getMiniMockInfo:'.$date;

    if (($miniMock = cacheGet($cacheKey)) == null) {
        $miniMock = array();

        $todayMokao = $cmsdb->getRow('SELECT `id`, `persons_num`, `name` FROM `qb_paper_mokao` WHERE `publish_date`=:date',
            array(':date' => $date));

        if (empty($todayMokao))
            return false;

        $miniMock['id'] = $todayMokao->id;
        $miniMock['name'] = $todayMokao->name;
        $miniMock['persons_num'] = intval($todayMokao->persons_num);
        $miniMock['description'] = getPaperType('天天模考')->remark;

        //mini模考分数统计
        $scores = (array)$cmsdb->getCol('SELECT `evaluate` FROM `qb_relationship_user_paper` WHERE `paper_id`=:paper_id AND `paper_type`=1 AND `status`=1',
            array(':paper_id' => $todayMokao->id));

        $rank = array();
        foreach ($scores as $score) {
            $score = intval($score);

            if (isset($rank[$score])) {
                $rank[$score]++;
            } else {
                $rank[$score] = 1;
            }
        }
        $miniMock['rank'] = $rank;

        cacheAdd($cacheKey, $miniMock, 24*60*60);
    }

    return $miniMock;
}

/**
 * 获取mini模考的用户答题信息
 *
 * @param $paperId
 * @return array|bool
 */
function getMiniMockUsers($paperId) {
    global $cmsdb;

    $cacheKey = 'func:getMiniMockUsers:'.$paperId;

    if (($records = cacheGet($cacheKey)) == null) {
        $data = (array)$cmsdb->getResults('SELECT `id`, `user_id`, `status`, `evaluate` FROM `qb_relationship_user_paper` 
        WHERE `paper_id`=:paper_id AND `paper_type`=1', array(':paper_id' => $paperId));

        if (empty($data))
            return false;

        $records = array();
        foreach ($data as $d) {
            $records[$d->user_id] = array(
                'id' => $d->id,
                'status' => $d->status,
                'evaluate' => $d->evaluate
            );
        }

        cacheAdd($cacheKey, $records, 24*60*60);
    }

    return $records;
}


/**
 * 获取试卷标题
 *
 * @param string $paperType 试卷类型
 * @param int $paperId 试卷id
 * @return string
 */
function getPaperName($paperType, $paperId) {
    global $cmsdb;

    $cacheKey = "func:getPaperName:{$paperType}:{$paperId}";

    if (($paperName = cacheGet($cacheKey)) == null) {

        switch ($paperType) {
            case 'mokao':
                $paperName = $cmsdb->getVar('SELECT `name` FROM `qb_paper_mokao` WHERE `id`=:paper_id',
                    array(':paper_id' => $paperId));
                break;

            case 'entire':
            case 'evaluate':
                $paperName = $cmsdb->getVar('SELECT `name` FROM `original_ztk_papers` WHERE `id`=:paper_id',
                    array(':paper_id' => $paperId));
                break;

            case 'mock':
                $paperName = getMockInfo($paperId)->paper_name;
                break;

            default:
                return '';
        }

        cacheAdd($cacheKey, $paperName, 24*60*60);
    }

    return $paperName;
}

/**
 * 获取模考信息
 *
 * @param $mockId
 * @return Object
 */
function getMockInfo($mockId) {
    global $cmsdb;

    $cacheKey = 'func:getMockInfo:'.$mockId;
    if (($mock = cacheGet($cacheKey)) == null) {
        $mock = $cmsdb->getRow("SELECT * FROM `data_test_paper` WHERE FIND_IN_SET('{$mockId}', `papers`)>0");
        if (empty($mock)) {
            $mock = $cmsdb->getRow('SELECT * FROM `data_test_paper` WHERE `id`=:paper_id', [':paper_id' => $mockId]);
        }

        cacheAdd($cacheKey, $mock, 24*60*60);
    }

    return $mock;
}

/**
 * 检测是否支持新版知识点结构
 *
 * @param $terminalType
 * @param $appVersion
 * @return bool
 */
function shouldShowNewNoteHierarchy($terminalType, $appVersion) {
    if ($terminalType == 'android_phone' && version_cmp($appVersion, '1.7') >= 0
        || $terminalType == 'iOS_phone' && version_cmp($appVersion, '2.5') >= 0
        || $terminalType == 'pc_web' || $terminalType == 'mobile_web') {
        return true;
    }

    return false;
}